iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0
Modern Web

【這些年我似是非懂的 Javascript】系列 第 4

【這些年我似是非懂的 Javascript】Day 4 - 你一定可以入的了門 #下篇

  • 分享至 

  • xImage
  •  

圖片來源

嗨各位今天來接續昨天那篇
【這些年我不懂的 Javascript】Day 3 - 你一定可以入的了門 #上篇

今日學習清單

  • 變數
  • 函式的範疇
  • 拉升 (Hosting)
  • 巢狀範疇
  • 條件式
    • If .. else .. if
    • Switch case
    • 三元運算子
  • 嚴格模式 ( Strict Mode )
  • 作為值的函式
  • 馬上被調用的函式 (IIFE)
  • Closure (閉包)
  • 模組
  • this 識別字
  • 原型
  • 舊功能與新特色
    • Polyfilling
    • Transpiling

Note:
以上大多數是屬於大概帶過,如果有涉略到後續幾本書的範圍,通常會講得很粗略,請見諒。


變數

在 JS 中變數 (包括函式名稱) 必須要是有效的識別字
基本上就是以 a-zA-Z$ 或是 _ 開頭,之後都可以包含以上這些加上 0-9,有些特定的字不能當作變數,就稱為保留字

函式的範疇

在函式內宣告任一變數或是函式,都只能在該函式使用。
有點像不可侵犯的領域(X

範例如下

function initMyName(){
    var name = 'Robin';
    function initMyage(){
        var age = '18';
    }
}

console.log(name) // ReferenceError
console.log(age) // ReferenceError
console.log(initMyage) // ReferenceError

拉升 (Hoisting)

當你使用 var 宣告變數時,他就會把你宣告的這個動作拉到該範疇的最頂端。
不懂? 來看看範例

console.log(test)

答案是~
// ReferenceError: test is not defined

一切都合理!
那再來!

console.log(test)
var test;

相信聰明的你一定知道...
答案還是
// ReferenceError: test is not defined

如果你心中想說 我就知道,那恭喜你答錯了。

答案是undefined

簡單來說他實際上變的會有點像這樣

var test;
console.log(test)
test;

這邊只是大概提一下,書中說之後會再說xD (撇清責任)
不過我還是忍不住找了一下相關的文章,
有興趣可以看一下這篇,我覺得寫得非常詳細。
我知道你懂 hoisting,可是你了解到多深?
推爆!

巢狀範疇

當你宣告ㄧ個變數,那個範疇內的所有位置都可以用,就是較底層和較內層的部份都可以取得到外層宣告的變數,但是外層沒辦法存取內層所宣告的變數。
範例如下

function foo(){
    var a = 2;
    function boo(){
        var b = 3;
        console.log(a) //2
    }
    console.log(b) // ReferenceError
}

條件式

除了前面提過的 if,你應該還要知道在 JS 中其他的條件式。

If .. else .. if

const age = 18;

if (age < 18){
    console.log('幼齒')
}
else if(age >18){
    console.log('成年囉')
}
else {
    console.log('剛好滿18')
}

雖然這樣看起來可以 work 但是顯得有點麻煩,因為你還是一直對 age 做判斷,有沒有更棒的判斷方式呢?

答案是有的!就是 switch case

Switch case

來改一下上面的範例

const age = 18;
switch(age) {
    case <18:
        console.log('幼齒');
        break;
    case >18:
        console.log('成年囉');
        break;
    default:
        console.log('剛好滿18');
}

基本上這樣看起來就是會比較能懂原來你這包全部都是對 age 做條件式的判斷。

那如果說是判斷沒那麼多東西的,寫 if else 和 switch 好像又有點尷尬怎麼辦,有沒有看起來可讀性更高而且更方便的呢?
有的!就是三元運算子

三元運算子

來直接看一下範例

const robin = 'boy';
(robin === 'boy')? console.log('男生啦'): console.log('女生啦');

基本上用法就是

(條件式) ? <如果是的話要幹嘛> : <不是的話要幹嘛>

是不是覺得~
原來那麼簡單呀!

嚴格模式 ( Strict Mode )

在 ES5 時引進了 "嚴格模式",簡單來說就是...

他會嚴厲的指責你幹嘛這樣幹嘛那樣的。
會叫你依照比較安全的且適當的,而且通常能夠使你的程式碼被引擎最佳化,所以聽到這是不是該把所有程式都加入 strict mode 了呢!?
心動不如馬上行動!

strict mode 是選擇性的你可以使他整個檔案都是 strict mode 也可以使單一 function 變成 strict 模式,這取決於你放哪個地方,如以下範例。

整個 file 使用 strict mode

'use strict';
// 整個 file 都是 strict mode

function testA(){
    // 這邊有使用 strict mode
    
    function testB(){
        // 這邊有使用 strict mode
    }
}
// 這邊有使用 strict mode

部分 function 使用 strict mode


function testA(){
    'use strict';
    // 這邊有使用 strict mode
    
    function testB(){
        // 這邊有使用 strict mode
    }
}
// 這邊沒有使用 strict mode

作為值的函式

就是把 function 丟進去給一個變數當值的意思,
直接看一下範例就知道我在說啥了~

var boo = function foo(){
    console.log("我愛一條柴")
}

boo() //我愛一條柴

馬上被調用的函式 (IIFE)

就是馬上被執行的函式,一般我們寫 function 都不會馬上被執行,一定是要被調用他才會執行,那如何做到呢?請看以下範例。

(function foo(){
    console.log('我直接被執行啦~')
})(); // 我直接被執行啦~

我個人滿常要寫快速的測試時會用到。

Closure (閉包)

這是大部分學 JS 最不能被理解的一塊,這邊之後會在範疇那本書會更深一點的講解,這邊簡單用以下範例介紹~

function makeAdder(x) {
  function add(y) {
    return x + y
  }
  return add
}
const addOne = makeAdder(1);
console.log(addOne(123)) // 124

看懂了嗎?
基本上就是在函式執行完畢(在上面的範例是指 makeAdder)之後,讓你可以"保留"該儲存的變數在該函式的範疇的一種方式。
以上面範例來說我就是將 1 這個數先暫存起來並且暫放在 addOne 這個變數,然後在執行他時帶了 123 這個值進去讓他加總得出 124 這個答案。

模組

在剛剛提到的閉包中最常見的手法就是模組模式,他能夠讓你定義私有的變數和函式,讓他們隱藏起來並開放可以從外部取用的公開 API。
請看以下範例

function User(){
    let name, pwd;
    
    function doRegist(userName, userPwd) {
        userName = name;
        userPwd = pwd;
        // do something 
    }
    
    const publicAPI = {
        regist: doRegist;
    }
    return publicAPI;
}

const robin = User();
robin.regist('robin', 'qq12345678qq')

關於這塊與閉包之後的文章還會提到(因為書上是這樣說的xDD)
這邊還是以簡單帶過讓大家知道可以這樣用。

this 識別字

this 指向誰,這個是連我到目前有時候都會有點搞不清楚的東西,
但是我有看到此系列的某本書名直接標題就是關於 this 想必也是需要花一點時間研讀,這邊簡單帶過。(因為我也不是很了解 Orz)
大部分時候的 this 是取決於"被誰呼叫",

function foo() {
  console.log(this.bar);
}

var bar = 'global';

var obj1 = {
  bar: 'obj1',
  foo: foo,
};

var obj2 = {
  bar: 'obj2',
};

foo(); // 'global'
obj1.foo(); // 'obj1'
foo.call(obj2); // 'obj2'
new foo(); // undefined

注意前面的範例中的最後四行,

  1. 不是 strict mode 時 foo() 會將 this 設定成全域物件,反之他則會變成 undefined,所以當你在 strict 模式中你會得到一個錯誤~
    但是在這裡你會拿到 global 這個值~
    因為前面說了在這邊他會把 this 變成全域物件。
  2. obj1.foo() 這邊他把 this 設為了 obj1 物件。
  3. foo.call(obj2) 把 this 設為了 obj2 物件。
  4. new foo() 把 this 產生一個全新的空物件。

如果你還是有點不懂...
沒關係持續關注訂閱加分享開啟小鈴鐺,
我之後在讀到那本書的時候會尋覓更好的譬喻 Orz

原型

這個是我目前也不太了解的一塊,大概知道他是什麼但是又不是那麼了解,但是又算滿常見的,這部分也是在 this 那本書會有詳細的說明,這邊也是簡單帶一下,基本上這個東西就是如果該物件的特性(你也可以想成功能啦),如果有缺少時的後備機制。
範例如下

const foo = {
    a: 88
}

var bar = Object.create(foo);

bar.b = 'Robin is handsome';

bar.b; // Robin is handsome
bar.a; // 88

bar.b 沒什麼好說的是我自己新增的, bar.a他就會去委派給 foo,也就是實際上 bar 不存在這個特性,但是如果有人用了他就去尋找看看 foo 有沒有這個東西,聽起來滿妙的。

舊功能與新特色

在 JS 版本的更新的情況下,部分舊版的瀏覽器並不支援新的功能,或是新的功能根本沒有在穩定版的瀏覽器實作。
這邊就來分享兩種可以將 JS 較新的功能帶到較舊的瀏覽器

Polyfilling

就是一個較新的功能定義但是卻產生具有相同的行為,例如 ES6 定義了一個功能為 Number..isNaN(..) 主要用來提供檢查是否為 NaN,棄用原本的 isNaN(..) ,這時我們就可以說我們可以輕易 polyfill 這個工具,而你不用管使用者使用的是不是支援 ES6 的瀏覽器如下範例改善 isNaN

if(!Number.isNaN){
    Number.isNaN = function isNaN(x){
        return x !==x;
    }
}

可是在實作時你可能會有疏失所以比較好的方式是使用一些可以幫你嚴格審核的函示庫書中提到 es5-shimes6-shim 可供使用。

Transpiling

那如果我沒辦法 polyfill 該語言的新語法怎麼辦,但是在舊版的瀏覽器中他又看不懂新語法導致會直接拋出錯誤。
此時就有一種工具是可以幫你把新語法轉換成等值的舊語法,
我只能說...
根本黑科技啊!!!

最常見的工具就是 BabelTraceur
至於我們為什麼要那麼麻煩使用新的語法編寫然後還要轉換到舊語法,有以下幾個重要的理由。

  1. 新語法中的設計是為了讓你的程式碼更好閱讀,科技是會進步的(X),不只是為了你個人維護,還為了你的團隊著想。
  2. 你如果為了舊的瀏覽器奮鬥,進行 transpile,並且提供新語法給最新的瀏覽器,你除了能獲得新語法的效能最佳化,也可以為瀏覽器的供應商有更多的程式碼可以幫他們測試,並讓他們可以實作以及最佳化。
  3. 早點使用可以讓 JS 委員會能夠提早發現問題,可能是新的語法有設計上的問題,就能避免像是之前提的 typeof null; // Object 一樣,可能無法修補一修補就會造成更大的漏洞。

ES6 提供了一個新的語法是預設參數值如下

function foo(name = 'robin'){
    console.log(name);
}

foo(); // robin
foo('ron'); // ron

那如果要自己 transpiling 該怎麼做?

function foo(){
    const name = arguments[0] !== void 0 ? arguments[0] : 'robin';
    console.log(name);
}

就是檢查 arguments[0] 是否為 undefined 如果是就給 robin 這個預設值。


今天文章到此入門也到此寫一寫越來越不像入門 xDD
不過沒關係,我自己學習起來看不懂的地方搞懂就是賺到(X

讀到這感覺目前都不是這本書的主軸,但是還是符合他說的
導讀,型別與文法,目前是導讀 xD
如果你覺得痛苦...
沒關係你還有我,因為我也...還好 (想不到吧)
感謝你讀到這。
明天見~


參考來源:

你所不知道的 JS|導讀,型別與文法 (You Don't Know JS: Up & Going)


上一篇
【這些年我似是非懂的 Javascript】Day 3 - 你一定可以入的了門 #上篇
下一篇
【這些年我似是非懂的 Javascript】Day 5 - 飽受爭議的型別
系列文
【這些年我似是非懂的 Javascript】34
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
fillano
iT邦超人 1 級 ‧ 2020-09-18 06:57:23

Hosting -> Hoisting?

Robin iT邦新手 2 級 ‧ 2020-09-18 08:25:47 檢舉

對xD 我打錯了/images/emoticon/emoticon02.gif
已更新,感謝糾正 xDDD

我要留言

立即登入留言